import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Article, ArticleStatus } from '@/modules/article/entities/article.entity';
import { LessThanOrEqual, Repository, SelectQueryBuilder } from 'typeorm';
import {
  CreateArticleDto,
  DataTableOperatorFilterMetaData,
  QueryArticleDto,
  QueryListDto,
  UpdateArticleDto,
  UpdateArticleStatusDto,
} from '@/modules/article/dto/dto';
import { ArticleCategory } from '@/modules/article/article_category/entities/category.entity';
import { ArticleTag } from '@/modules/article/article_tag/entities/tag.entity';
import { User } from '@/modules/user/entities/user.entity';

@Injectable()
export class ArticleService {
  constructor(
    @InjectRepository(Article)
    private readonly articleRepository: Repository<Article>,
    @InjectRepository(ArticleCategory)
    private readonly categoryRepository: Repository<ArticleCategory>,
    @InjectRepository(ArticleTag)
    private readonly tagRepository: Repository<ArticleTag>,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  async articleList(data: QueryArticleDto) {
    const [articleList, total] = await this.articleRepository.findAndCount({
      where: {
        article_status: ArticleStatus.PUBLISHED,
        article_public_date: LessThanOrEqual(new Date()),
      },
      relations: { author: true, tags: true, categories: true },
      order: { [data.orderBy || 'update_date']: data.sort ?? 'desc' },
      skip: (data.pageNo - 1) * data.pageSize,
      take: data.pageSize,
    });
    return {
      list: articleList,
      total,
      pageNo: data.pageNo,
      pageSize: data.pageSize,
    };
  }

  async articleFindOne(id: number, view: boolean) {
    // 使用QueryBuilder查找，添加查询article_content
    if (view) {
      // 阅读量 +1
      await this.articleRepository.increment({ article_id: id }, 'article_views', 1);
    }
    return await this.articleRepository
      .createQueryBuilder('article')
      .where({ article_id: id })
      .leftJoinAndSelect('article.author', 'author')
      .leftJoinAndSelect('article.tags', 'tags')
      .leftJoinAndSelect('article.categories', 'categories')
      .addSelect('article.article_content')
      .getOne();
  }

  async articleCreate(data: CreateArticleDto, userId: number) {
    // 判断用户是否存在
    const author = await this.userRepository.findOne({
      where: { id: userId },
    });
    if (!author) {
      throw new HttpException('用户不存在', HttpStatus.BAD_REQUEST);
    }

    // 判断分类是否存在
    const categories = await this.categoryRepository
      .createQueryBuilder()
      .whereInIds(data.categoryIds)
      .getMany();

    if (categories.length !== data.categoryIds.length) {
      throw new HttpException('分类不存在', HttpStatus.BAD_REQUEST);
    }

    // 判断标签是否存在
    const tags = await this.tagRepository.createQueryBuilder().whereInIds(data.tagIds).getMany();

    if (tags.length !== data.tagIds.length) {
      throw new HttpException('标签不存在', HttpStatus.BAD_REQUEST);
    }

    // 创建文章
    await this.articleRepository.save({
      author,
      article_title: data.title,
      article_description: data.description,
      article_content: data.content,
      cover_src: data.coverSrc,
      article_status: data.status,
      categories,
      tags,
      article_public_date: data.publicDate,
    });
    return '创建成功';
  }

  async articleUpdate(id: number, data: UpdateArticleDto) {
    // 判断文章是否存在
    const article = await this.articleRepository.findOne({
      where: { article_id: id },
      relations: { categories: true, tags: true },
    });

    if (!article) {
      throw new HttpException('文章不存在', HttpStatus.BAD_REQUEST);
    }

    let categories: ArticleCategory[] = article.categories;
    // 判断分类是否存在
    if (data.categoryIds && data.categoryIds.length > 0) {
      categories = await this.categoryRepository
        .createQueryBuilder()
        .whereInIds(data.categoryIds)
        .getMany();

      if (categories.length !== data.categoryIds.length) {
        throw new HttpException('分类不存在', HttpStatus.BAD_REQUEST);
      }
    }
    // 更新分类
    await this.articleRepository
      .createQueryBuilder()
      .relation(Article, 'categories')
      .of(id)
      .addAndRemove(categories, article.categories);

    let tags: ArticleTag[] = article.tags;
    // 判断标签是否存在
    if (data.tagIds && data.tagIds.length > 0) {
      tags = await this.tagRepository.createQueryBuilder().whereInIds(data.tagIds).getMany();

      if (tags.length !== data.tagIds.length) {
        throw new HttpException('标签不存在', HttpStatus.BAD_REQUEST);
      }
    }
    // 更新标签
    await this.articleRepository
      .createQueryBuilder()
      .relation(Article, 'tags')
      .of(id)
      .addAndRemove(tags, article.tags);

    // 更新文章其他信息
    await this.articleRepository.update(
      { article_id: id },
      {
        article_title: data.title,
        article_description: data.description,
        article_content: data.content,
      },
    );
    return '更新成功';
  }

  async articleRemove(ids: number[]) {
    // 检查文章是否存在
    const articles = await this.articleRepository.createQueryBuilder().whereInIds(ids).getMany();

    if (articles.length !== ids.length) {
      throw new HttpException('文章不存在', HttpStatus.BAD_REQUEST);
    }

    // 批量软删除
    return await this.articleRepository.softDelete(ids);
  }

  async articleRemoveOne(id: number) {
    // 检查文章是否存在
    const article = await this.articleRepository.findOne({
      where: { article_id: id },
    });

    if (!article) {
      throw new HttpException('文章不存在', HttpStatus.BAD_REQUEST);
    }
    return await this.articleRepository.softDelete(id);
  }

  async articlesTotal() {
    // 获取公开的文章数量
    const articleTotal = await this.articleRepository.count({
      where: {
        article_status: ArticleStatus.PUBLISHED,
        article_public_date: LessThanOrEqual(new Date()),
      },
    });
    // 获取所有文章的总浏览量
    const { viewsTotal } = await this.articleRepository
      .createQueryBuilder('article')
      .select('SUM(article.article_views)', 'viewsTotal')
      .getRawOne<{ viewsTotal: string }>();
    // 获取所有文章的总点赞数
    const { likeTotal } = await this.articleRepository
      .createQueryBuilder('article')
      .select('SUM(article.article_like_count)', 'likeTotal')
      .getRawOne<{ likeTotal: string }>();
    // 获取所有文章的总评论数
    const { commentTotal } = await this.articleRepository
      .createQueryBuilder('article')
      .select('SUM(article.article_comment_count)', 'commentTotal')
      .getRawOne<{ commentTotal: string }>();

    return {
      articleTotal,
      viewsTotal: Number(viewsTotal),
      likeTotal: Number(likeTotal),
      commentTotal: Number(commentTotal),
    };
  }

  async articleQuery({ lazyParams }: QueryListDto) {
    const pageSize = lazyParams.rows;
    const pageNo = 'page' in lazyParams ? lazyParams.page : 0;

    const queryBuilder = this.articleRepository.createQueryBuilder('article');

    queryBuilder.leftJoinAndSelect('article.author', 'author');
    queryBuilder.leftJoinAndSelect('article.tags', 'tags');
    queryBuilder.leftJoinAndSelect('article.categories', 'categories');

    // 处理过滤
    this.handleFilters(queryBuilder, lazyParams.filters);

    // 处理排序
    this.handleSort(
      queryBuilder,
      lazyParams.sortField || 'article_id', // 没有排序字段用 article_id 排序
      lazyParams.sortOrder === -1 ? 'DESC' : 'ASC',
    );

    queryBuilder.skip(pageNo * pageSize);
    queryBuilder.take(pageSize);

    const [articleList, total] = await queryBuilder.getManyAndCount();
    return {
      list: articleList,
      total,
      pageNo: pageNo,
      pageSize: pageSize,
    };
  }

  private handleSort(
    queryBuilder: SelectQueryBuilder<Article>,
    sortField: string,
    sortOrder: 'ASC' | 'DESC',
  ) {
    // 根据排序字段不同应用不同规则
    if (sortField.includes('author')) {
      // 根据作者的昵称排序
      queryBuilder.orderBy('author.nickName', sortOrder);
    } else if (sortField.includes('tags')) {
      // 根据第一个标签的名称排序并将没有标签的文章排在一起
      queryBuilder.addSelect((subQuery) => {
        return subQuery
          .select('tags.tag_name', 'tag_name')
          .from('article_tags_relation', 'relation')
          .innerJoin(ArticleTag, 'tags', 'tags.tag_id = relation.tag_id')
          .where('relation.article_id = article.article_id')
          .orderBy('tags.tag_name', 'ASC')
          .limit(1);
      }, 'tag_name');
      queryBuilder.orderBy('tag_name', sortOrder);
    } else if (sortField.includes('categories')) {
      // 根据第一个分类的名称排序并将没有分类的文章排在一起
      queryBuilder.addSelect((subQuery) => {
        return subQuery
          .select('categories.category_name', 'category_name')
          .from('article_categories_relation', 'relation')
          .innerJoin(ArticleCategory, 'categories', 'categories.category_id = relation.category_id')
          .where('relation.article_id = article.article_id')
          .orderBy('categories.category_name', 'ASC')
          .limit(1);
      }, 'category_name');
      queryBuilder.orderBy('category_name', sortOrder);
    } else {
      // 默认排序
      queryBuilder.orderBy(`article.${sortField}`, sortOrder);
    }
  }

  private handleFilters(
    queryBuilder: SelectQueryBuilder<Article>,
    filters: Record<string, DataTableOperatorFilterMetaData>,
  ) {
    // 循环 filters 对象
    for (const key of Object.keys(filters)) {
      const filter = filters[key];

      // 如果 value 为 null 则跳过
      if (filter.value === null) continue;

      // 处理 key=global
      if (key === 'global') {
        switch (filter.matchMode) {
          case 'contains':
            // 全局模糊匹配 key: global
            // 全局模糊匹配，匹配文章标题、文章描述、作者昵称、标签名称、分类名称
            queryBuilder
              .orWhere(`article.article_title LIKE :value`, {
                value: `%${filter.value}%`,
              })
              .orWhere(`article.article_description LIKE :value`, {
                value: `%${filter.value}%`,
              })
              .orWhere(`author.nickName LIKE :value`, {
                value: `%${filter.value}%`,
              })
              .orWhere(`tags.tag_name LIKE :value`, {
                value: `%${filter.value}%`,
              })
              .orWhere(`categories.category_name LIKE :value`, {
                value: `%${filter.value}%`,
              });
            break;
        }
        continue;
      }

      // 处理其他 key
      // 根据 matchMode 进行匹配
      switch (filter.matchMode) {
        case 'contains':
          // 模糊匹配 包含
          queryBuilder.andWhere(`${key} LIKE :value`, {
            value: `%${filter.value}%`,
          });
          break;
        case 'dateIs':
          // 日期类型的匹配 只匹配日期
          const date = new Date(filter.value);
          const startOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
          const endOfDay = new Date(
            date.getFullYear(),
            date.getMonth(),
            date.getDate(),
            23,
            59,
            59,
            999,
          );
          queryBuilder.andWhere(`article.${key} BETWEEN :start AND :end`, {
            start: startOfDay,
            end: endOfDay,
          });
          break;
      }
    }
  }

  async articleUpdateStatus(id: number, data: UpdateArticleStatusDto) {
    // 判断文章是否存在
    const article = await this.articleRepository.findOne({
      where: { article_id: id },
    });

    if (!article) {
      throw new HttpException('文章不存在', HttpStatus.BAD_REQUEST);
    }

    // 更新文章状态
    await this.articleRepository.update({ article_id: id }, { article_status: data.status });
    return '更新成功';
  }

  async articleDraft(data: QueryArticleDto, userId: number) {
    // 获取用户的草稿文章
    const [list, total] = await this.articleRepository.findAndCount({
      where: {
        article_status: ArticleStatus.DRAFT,
        author: { id: userId },
      },
      relations: { author: true, tags: true, categories: true },
      order: { [data.orderBy || 'update_date']: data.sort },
      skip: (data.pageNo - 1) * data.pageSize,
      take: data.pageSize,
    });
    return {
      list,
      total,
      pageNo: data.pageNo,
      pageSize: data.pageSize,
    };
  }

  async articleMy(data: QueryArticleDto, userId: number) {
    // 获取用户的文章
    const [list, total] = await this.articleRepository.findAndCount({
      where: {
        author: { id: userId },
      },
      relations: { author: true, tags: true, categories: true },
      order: { [data.orderBy || 'update_date']: data.sort },
      skip: (data.pageNo - 1) * data.pageSize,
      take: data.pageSize,
    });
    return {
      list,
      total,
      pageNo: data.pageNo,
      pageSize: data.pageSize,
    };
  }

  async articleLike(id: number) {
    // 点赞文章
    await this.articleRepository.increment({ article_id: id }, 'article_like_count', 1);
    return '点赞成功';
  }
}
